﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using Bling.WPF;
using Bling.DSL;
using Bling.Geom;
using Bling.Util;
using linq = Microsoft.Linq.Expressions;

namespace Bling.DSL {
  public partial class IntBl {
    private static readonly Operator<int, double> FactorialOp = new Operator<int, double>(new OperatorMarker("Factorial") {
      Unary = (a) => "Bling.Util.MyMath.Factorial(" + a + ")",
    }) {
      Apply = (a) => MyMath.Factorial(a),
      Linq = (a) => linq.Expression.Call(typeof(MyMath), "Factorial", new Type[] { }, a),
    };
    public DoubleBl Factorial {
      get {
        return FactorialOp.Make(this);
      }
    }

  }
  public abstract partial class MultXBl<T, BRAND> : AddBl<T, BRAND> where BRAND : MultXBl<T, BRAND> {
    public BRAND BezierTerm(DoubleBl t, int n, int i) {
      return this * t.Bernstein(n, i);
    }
    /// <summary>
    /// Calculate the value along a Bezier path at percent t.
    /// </summary>
    /// <param name="t">Perect progress along the Bezier path.</param>
    /// <param name="first">What term should occur first (object affects inversion).</param>
    /// <param name="p">the control points</param>
    /// <returns></returns>
    public static BRAND Bezier(DoubleBl t, int first, params BRAND[] p) {
      BRAND result = ToBrand(new Constant<T>(default(T)));
      for (int i = 0; i < p.Length; i++) {
        var j = (i + first) % p.Length;
        result += p[j] * t.Bernstein(p.Length - 1, j);
      }
      return result;
    }
    public static BRAND Bezier(DoubleBl t, params BRAND[] ps) {
      for (int i = 1; i < ps.Length; i++) 
        for (int j = 0; j < ps.Length - i; j++) 
          ps[j] = ps[j] * (1 - t) + ps[j + 1] * t;

      return ps[0];
    }

   
    public static BRAND Bezier0(DoubleBl t, IntBl first, params BRAND[] p) {
      BRAND result = ToBrand(new Constant<T>(default(T)));
      for (int i = 0; i < p.Length; i++) {
        result += p[i] * t.Bernstein(p.Length - 1, (first + i) % p.Length);
      }
      return result;
    }


    /// <summary>
    /// Simulate momentum using Verlet. 
    /// </summary>
    /// <param name="loss">Percent of momentum that is lost per step; e.g., .01 causes 1% loss in momentum per time step.</param>
    /// <param name="OldValue">Previous computed value.</param>
    /// <returns>New now value.</returns>
    public BRAND VerletStep(DoubleBl loss, BRAND OldValue) {
      return this * (2d - loss) - OldValue * (1d - loss);
    }
    /// <summary>
    /// Establish verlet as step for this property.
    /// </summary>
    /// <param name="loss">Percent of momentum that is lost per step; e.g., .01 causes 1% loss in momentum per time step.</param>
    public void VerletStep(DoubleBl loss) {
      this.Step = (now, old) => now.VerletStep(loss, old);
    }
  }
  public partial class DoubleBl {
    /// <summary>
    /// Determine if this value is between min and max.
    /// </summary>
    /// <param name="min"></param>
    /// <param name="max"></param>
    /// <returns>True iff this is greater than min and less than max.</returns>
    public BoolBl Between(DoubleBl min, DoubleBl max) {
      return (this >= min & this <= max) | (this >= max & this <= min);
      /*
      var diffA = max - min;
      var diffB = this - min;

      return diffA.Sign != diffB.Sign & diffB.Abs <= diffA.Abs;
       */
    }
    public static bool Between(double self, double min, double max) {
      var diffA = max - min;
      var diffB = self - min;
      return Math.Sign(diffA) != Math.Sign(diffB) && Math.Abs(diffB) <= Math.Abs(diffA);
    }


    /// <summary>
    /// Calculate the Bernstein coefficients of degree n.
    /// </summary>
    /// <param name="n"></param>
    /// <param name="i">argument</param>
    /// <returns></returns>
    public DoubleBl Bernstein(IntBl n, IntBl i) {
      if(i.CurrentValue==0)
        return (1d - this).Pow(n);
      else
        return this.Pow(i) * (1d - this).Pow(n - i) * BinomialCoefficient(n, i);
    }
    /// <summary>
    /// Calculate the Binomial coefficients of  n and k:C(n,k).
    /// </summary>
    public static DoubleBl BinomialCoefficient(IntBl n, IntBl k) {
      //if (k == 0 || n == k) return 1;
     // return BinomialCoefficient(n - 1, k - 1) + BinomialCoefficient(n - 1, k);
      //return MyMath.Factorial(n) / (MyMath.Factorial(k) * MyMath.Factorial(n - k));
      return n.Factorial / (k.Factorial * (n - k).Factorial);
    }


    /// <summary>
    /// calculate the Bezier value with control points p[] in type DoubleBl
    /// </summary>
    public DoubleBl Bezier(int first, params DoubleBl[] p) { return DoubleBl.Bezier(this, first, p); }
    /// <summary>
    /// calculate the Bezier value with control points p[] in type DoubleBl
    /// </summary>
    public DoubleBl Bezier(params DoubleBl[] p) { return Bezier(0, p); }
    /// <summary>
    /// calculate the Bezier value with control points p[] in type PointBl
    /// </summary>
    public PointBl Bezier(int first, params PointBl[] p) { return PointBl.Bezier(this, first, p); }
    /// <summary>
    /// calculate the Bezier value with control points p[] in type PointBl
    /// </summary>
    public PointBl Bezier(params PointBl[] p) { return Bezier(0, p); }

    /// <summary>
    /// calculate the Bezier value with control points p[] in type ColorBl
    /// </summary>
    public ColorBl Bezier(int first, params ColorBl[] p) { return ColorBl.Bezier(this, first, p); }
    /// <summary>
    /// calculate the Bezier value with control points p[] in type ColorBl
    /// </summary>
    public ColorBl Bezier(params ColorBl[] p) { return Bezier(0, p); }

    /// <summary>
    /// calculate the Bezier value with control points p[] in type Point3DBl
    /// </summary>
    public Point3DBl Bezier(int first, params Point3DBl[] p) { return Point3DBl.Bezier(this, first, p); }
    /// <summary>
    /// calculate the Bezier value with control points p[] in type Point3DBl
    /// </summary>
    public Point3DBl Bezier(params Point3DBl[] p) { return Bezier(0, p); }
  }
  public abstract partial class PointBl<T, BRAND, ARITY> : RealTupleBl<T, BRAND, ARITY>
    where BRAND : PointBl<T, BRAND, ARITY>
    where ARITY : ArityInfo<T, ARITY>, new() {

    /*
    public static BRAND Intersect(BRAND P1, BRAND P2, BRAND P3, BRAND P4) {
      var ua = (P4[0] - P3[0]) * (P1[1] - P3[1]) - (P4[1] - P3[1]) * (P1[0] - P3[0]);
      var div = ((P4[1] - P3[1]) * (P2[0] - P1[0]) - (P4[0] - P3[0]) * (P2[1] - P1[1]));
      ua = ua / div;
      var ret = P1 + ((P2 - P1) * ua);
      ret = (ua < 0 | ua > 1 | ua.IsNaN | !ret.Between(P3, P4)).Condition(PointBl.NaN, ret);
      return ret;
    }
    */


    private static readonly Operator<T, T, double, T> SpringOp = new Operator<T, T, double, T>(new OperatorMarker("Spring(")) {
      Expand = (Self, Center, Radius) => {
        
        BRAND v = ToBrand(Self)- ToBrand(Center);
        var l = v.Length;
        double[] d = new double[ArityInfo<T, ARITY>.Instance.Arity];
        d[0] = 1;
        for (int i = 1; i < d.Length; i++) d[i] = 0;

        BRAND unit = ToBrand(new Constant<T>(ArityInfo<T, ARITY>.Instance.ToT(d)));
        v = (l == 0).Condition0<BRAND>(unit, v);
        l = (l == 0).Condition(1, l);
        v = v / l * Radius.Bl();
        return v + ToBrand(Center);
      },
      XInverse1 = (Center, Other, Self, Radius) => {
        if (!Other.Equals(Self)) return null;
        return SpringOp.Make(Center, Self, Radius);
      },
    };
    /// <summary>
    /// Compute point on a circle that is closest to this point; e.g., they will have the same angle with respect to the circle's center. Useful in expressing springs.
    /// </summary>
    /// <param name="Center">Center of the circle.</param>
    /// <param name="Radius">Radius of the circle.</param>
    /// <returns>A point on the circle's perimeter that is closest to this point.</returns>
    public BRAND Spring(BRAND Center, DoubleBl Radius) {
      return ToBrand(SpringOp.Make(this, Center, Radius));
    }
    public BasePointBl Spring(BasePointBl Center, DoubleBl Radius) {
      return Spring((BRAND) Center, Radius);
    }
  }
  public partial interface BasePointBl : BaseBrand {
    BasePointBl Spring(BasePointBl Center, DoubleBl Radius);
  }


  public partial class PointBl {

    /// <summary>
    /// to determine if this point is between the point p0 and the p1
    /// </summary>
    /// <param name="p0"></param>
    /// <param name="p1"></param>
    /// <returns></returns>
    public BoolBl Between(PointBl p0, PointBl p1) {
      return X.Between(p0.X, p1.X) & Y.Between(p0.Y, p1.Y);
    }
    /*
    public static bool Between(Point self, Point p0, Point p1) {
      return DoubleBl.Between(self.X, p0.X, p1.X) && DoubleBl.Between(self.Y, p0.Y, p1.Y);
    }
     */


    public PointBl ClockwisePerp {
      get {
        return new PointBl(-Y, X);
      }
    }


    /// <summary>
    /// Gets or sets the angle of this point and (1,0) in radians type
    /// </summary>
    public RadianAngleBl Angle {
      //get { return new Point(1, 0).Bl().AngleBetween(this); }
      get { return RadianAngleBl.Angle(this); }
      set { Angle.Bind = value; }
    }
    /// <summary>
    /// Low-overhead angle conversion for percentage-relative points (useful in pixel shaders). 
    /// </summary>
    public RadianAngleBl SimpleAngle {
      get {
        return new RadianAngleBl(X.Atan2(Y));
      }
    }

    /// <summary>
    /// Gets or sets the angle of this point and other in radians type
    /// </summary>
    public RadianAngleBl AngleBetween(PointBl other) {
      return RadianAngleBl.AngleBetween(this, other);
    }

    /// <summary>
    /// dot product with the other point
    /// </summary>
    /// <param name="other"></param>
    /// <returns></returns>
    public DoubleBl Dot(PointBl other) {
      return X * other.X + Y * other.Y;
    }
    /// <summary>
    /// cross product with the other point
    /// </summary>
    /// <param name="other"></param>
    /// <returns></returns>
    public DoubleBl Cross(PointBl other) {
      // p.Y * q.X - p.X * q.Y, 
      // p.X * q.Y - p.Y * q.Y 

      return X * other.Y - Y * other.X;
    }


    public PointBl MaxRadius(PointBl center, DoubleBl Radius) {
      PointBl v = this - center;
      var l = v.Length;
      v = (l == 0).Condition(new PointBl(1, 0), v);
      l = (l == 0).Condition(1, l);
      v = v / l * Radius.Min(l);
      return v + center;
    }

    /// <summary>
    /// To determine where line(P1,P2) and line(P3,P4) intersect
    /// </summary>
    /// <param name="P1">the start point of the first line</param>
    /// <param name="P2">the end point of the first line</param>
    /// <param name="P3">the start point of the second line</param>
    /// <param name="P4">the end point of the second line</param>
    /// <returns></returns>
    public static PointBl Intersect(PointBl P1, PointBl P2, PointBl P3, PointBl P4) {
      var ua = (P4.X - P3.X) * (P1.Y - P3.Y) - (P4.Y - P3.Y) * (P1.X - P3.X);
      var div = ((P4.Y - P3.Y) * (P2.X - P1.X) - (P4.X - P3.X) * (P2.Y - P1.Y));
      ua = ua / div;
      var ret = P1 + ((P2 - P1) * ua);
      ret = (ua < 0 | ua > 1 | ua.IsNaN | !ret.Between(P3, P4)).Condition(PointBl.NaN, ret);
      return ret;
    }
    private static Func<Point,Point,Point,Point,Point> Intersect0;
    public static Point? Intersect(Point P1, Point P2, Point P3, Point P4) {
      if (Intersect0 == null) Intersect0 = PointBl.Func<Point, Point, Point, Point>(
        (p1, p2, p3, p4) => PointBl.Intersect(p1, p2, p3, p4));
      try {
        var p = Intersect0(P1, P2, P3, P4);
        if (double.IsNaN(p.X) || double.IsNaN(p.Y)) return null;
        return p;
      } catch (ArithmeticException e) { return null; }
    }

    /*
    public static Point? Intersect(Point P1, Point P2, Point P3, Point P4) {
      var ua = (P4.X - P3.X) * (P1.Y - P3.Y) - (P4.Y - P3.Y) * (P1.X - P3.X);
      var div = ((P4.Y - P3.Y) * (P2.X - P1.X) - (P4.X - P3.X) * (P2.Y - P1.Y));
      if (div == 0) return null;
      ua = ua / div;
      var ret = P1 + ((P2 - P1) * ua);
      if (ua < 0 || ua > 1) return null;
      if (PointBl.Between(ret, P3, P4)) return null;
      //ret = (ua < 0 | ua > 1 | ret.Between(P3, P4)) ? (new Point(double.NaN, double.NaN)) : (ret);
      return ret;
    }
     */

    /// <summary>
    /// to determine if this point and the other one are at the same side of line(a,b)
    /// </summary>
    /// <param name="other">to decide if the other point is at the same side of this point</param>
    /// <param name="a">the start point of the line</param>
    /// <param name="b">the end point of the line</param>
    /// <returns></returns>
    public BoolBl SameSide(PointBl other, PointBl a, PointBl b) {
      var ba = (b - a);
      var s0 = ba.Cross(this - a).Sign;
      var s1 = ba.Cross(other - a).Sign;
      return s0 == 0 | s1 == 0 | s0 == s1;
    }
    private static Func<Point, Point, Point, Point, bool> SameSide0;
    public static bool SameSide(Point Self, Point Other, Point A, Point B) {
      if (SameSide0 == null) SameSide0 = BoolBl.Func<Point, Point, Point, Point>((self, other, a, b) => self.Bl().SameSide(other, a, b));
      return SameSide0(Self, Other, A, B);
    }


    /// <summary>
    /// determine if this point within the triangle ABC
    /// </summary>
    /// <param name="A">the first vertex of triangle</param>
    /// <param name="B">the second vertex of triangle</param>
    /// <param name="C">the third vertex of triangle</param>
    /// <returns></returns>
    public BoolBl Within(PointBl A, PointBl B, PointBl C) {
      return this.SameSide(A, B, C) & (this.SameSide(B, A, C)) & (this.SameSide(C, A, B));
    }
    public static bool Within(Point Self, Point A, Point B, Point C) {
      return SameSide(Self, A, B, C) && (SameSide(Self, B, A, C)) && (SameSide(Self, C, A, B));
    }

    /// <summary>
    /// to determine if this point is inside the convex consists of P1, P2, P3 and P4 in 
    /// clockwise order or in conter-clockwise order    
    /// </summary>
    /// <param name="P1"></param>
    /// <param name="P2"></param>
    /// <param name="P3"></param>
    /// <param name="P4"></param>
    /// <returns></returns>        
    public BoolBl Within(PointBl P1, PointBl P2, PointBl P3, PointBl P4) {
      return (this.SameSide(P4, P1, P2) &
        this.SameSide(P1, P2, P3) &
        this.SameSide(P2, P3, P4) &
        this.SameSide(P3, P4, P1));
    }

    public static bool Within(Point Self, Point P1, Point P2, Point P3, Point P4) {
      return (SameSide(Self, P4, P1, P2) &
        SameSide(Self, P1, P2, P3) &
        SameSide(Self, P2, P3, P4) &
        SameSide(Self, P3, P4, P1));
    }


    /*
    public static bool Within(Point self, Point P1, Point P2, Point P3, Point P4) {
      if (Within0 == null) Within0 = BoolBl.Func<Point, Point, Point, Point>((p1, p2, p3, p4) => p1.Bl().Within(p2, p3, p4));
    }
    */

    /// <summary>
    /// to rotate this point with a specified angle around the specified center point
    /// </summary>
    /// <param name="center">the center point of rotation</param>
    /// <param name="angle">the angle to rotate</param>
    /// <returns></returns>
    public PointBl Rotate(PointBl center, RadianAngleBl angle) {
      var v = this - center;
      var a = v.Angle + angle;
      return center + a.ToPoint() * v.Length;
    }
    /// <summary>
    /// return the intersecting point of the height of the triangle (this,B,C) from this point to the base (B,C) and the base (B,C) 
    /// </summary>
    /// <param name="B">the start point of the base of the triangle</param>
    /// <param name="C">the end point of the base of the triangle</param>
    /// <returns></returns>
    public PointBl TriangleBase(PointBl B, PointBl C) {
      var A = this;
      var angle = ((C - B).Angle + .5.Bl().ToPI()).Inverse;
      var Bx = B.Rotate(A, angle);
      var Hx = new PointBl(A.X, Bx.Y);
      var H = Hx.Rotate(A, angle.Inverse);
      return H;
    }
    private static Func<Point, Point, Point, Point> TriangleBase0;
    public static Point TriangleBase(Point A, Point B, Point C) {
      if (TriangleBase0 == null) {
        TriangleBase0 = PointBl.Func<Point, Point, Point>((a,b,c) => a.Bl().TriangleBase(b,c));
      }
      return TriangleBase0(A, B, C);
    }

    public DoubleBl TriangleRatio(PointBl Start, PointBl Mid, PointBl End) {
      var areaA = this.TriangleArea(Mid, Start).Sqrt();
      var areaB = this.TriangleArea(Mid, End).Sqrt();

      var sum = areaA + areaB;


      return areaA / (areaA + areaB);
    }

    /// <summary>
    /// return the area of triangle ABC
    /// </summary>
    /// <param name="A">the first vertex of triangle</param>
    /// <param name="B">the second vertex of triangle</param>
    /// <param name="C">the third vertex of triangle</param>
    /// <returns></returns>
    public DoubleBl TriangleArea(PointBl B, PointBl C) {
      PointBl A = this;
      var t0 = (C.X - A.X) * (B.Y - A.Y);
      var t1 = (B.X - A.X) * (C.Y - A.Y);
      return .5 * (t0 - t1).Abs;
    }

    /// <summary>
    /// return the distance of this point to the line denoted by point start to end
    /// </summary>
    /// <param name="start">the start point of line</param>
    /// <param name="end">the end point of line</param>    
    /// <returns></returns>    
    public DoubleBl Distance(PointBl start, PointBl end) {
      var lineDir = (end - start).Normal;

      var lineDirP = new PointBl(-lineDir.Y, lineDir.X); //90 degree rotation with clockwise
      var lineC = -lineDirP.Dot(start);

      var distance = (this.Dot(lineDirP) + lineC);

      return distance;
    }

    public PointBl MirrorPoint(PointBl start, PointBl end) {
      var lineDir = (end - start).Normal;

      var lineDirP = new PointBl(-lineDir.Y, lineDir.X);
      var lineC = -lineDirP.Dot(start);

      var distance = (this.Dot(lineDirP) + lineC);

      var mirrorPoint = this - (2 * distance) * lineDirP;

      return mirrorPoint;
    }

    /// <summary>
    /// Translate this point based on four points of a quadrilateral as if the quadrilateral was a distorted rectangle.
    /// </summary>
    /// <param name="area">Total area of quadrilateral, defined as TriangleArea(points[0], points[1], points[2]) + TriangleArea(points[2], points[3], points[0]).</param>
    /// <param name="isIn">Whether or not this point is in the quadrilateral.</param>
    /// <param name="points">Four points that define the north, east, south, and west corner of the quadrilateral</param>
    /// <returns>A translated point as if the quadrilateral was a distorted rectangle.</returns>
    public PointBl Distort(DoubleBl area, out BoolBl isIn, params PointBl[] points) {
      var uv = this;
      var north = uv.TriangleArea(points[0], points[1]);
      var east = uv.TriangleArea(points[1], points[2]);
      var south = uv.TriangleArea(points[2], points[3]);
      var west = uv.TriangleArea(points[3], points[0]);
      var y = north / (north + south);
      var x = west / (west + east);
      var size = north + south + west + east;
      isIn = size <= area + .0000001;
      return new PointBl(x, y);
    }
    public PointBl Distort(DoubleBl area, out BoolBl isIn, DoubleBl ctl, params PointBl[] points) {
      var uv = this;
      var north = uv.TriangleArea(points[0], points[1]);
      var east = uv.TriangleArea(points[1], points[2]);
      var south = uv.TriangleArea(points[2], points[3]);
      var west = uv.TriangleArea(points[3], points[0]);
      var y = north / (north + south);
      var x = west / (west + east);
      var size = north + south + west + east;
      isIn = size <= area + .0000001 * ctl;
      return new PointBl(x, y);
    }
    public PointBl Distort(params PointBl[] points) {
      //var uv = this;
      var north = this.TriangleArea(points[0], points[1]);
      var east = this.TriangleArea(points[1], points[2]);
      var south = this.TriangleArea(points[2], points[3]);
      var west = this.TriangleArea(points[3], points[0]);
      var y = north / (north + south);
      var x = west / (west + east);
      return new PointBl(x, y);
    }

    /// <summary>
    /// return the length of the quadBezier curve from this to p2
    /// </summary>
    /// <param name="p1">the control point</param>
    /// <param name="p2">the end point</param>
    /// <returns></returns>
    public DoubleBl QuadBezierLength(PointBl p1, PointBl p2) {
      //var p0 = startPoint;
      //var p1 = source.Point1();
      //var p2 = source.Point2();
      var p0 = this;
      var a = p0 - (p1 * 2) + p2;
      var b = (p1 * 2) - (p0 * 2);

      var A = 4d * ((a.X).Square() + (a.Y).Square());
      var B = 4d * ((a.X * b.X) + (a.Y * b.Y));
      var C = (b.X.Square()) + (b.Y.Square());

      var Sabc = 2d * (A + B + C).Sqrt();
      var A_2 = A.Sqrt();
      var A_32 = A * A_2 * 2d;
      var C_2 = C.Sqrt() * 2d;
      var BA = B / A_2;
      return (A_32 * Sabc + A_2 * B * (Sabc - C_2) + (4 * C * A - B * B) * ((2d * A_2 + BA + Sabc) / (BA + C_2)).Log) / (4 * A_32);
    }

    /// <summary>
    /// make a thumb
    /// </summary>
    /// <param name="canvas">which canvas the thumb is on</param>
    /// <param name="color">the color of thumb</param>
    /// <param name="CanDrag">if this thumb can be dragged</param>
    /// <returns></returns>
    /*
    public static PointBl MakeThumb(Canvas canvas, BrushBl color, bool CanDrag) {
      var thumb = new Thumb() { Width = 10, Height = 10 };
      thumb.Bl().ZIndex = 10;
      thumb.Bl().Background = color;
      if (CanDrag) thumb.DragDelta += (x, y) => ((Thumb)thumb).Bl().LeftTop.Now += y.Delta();
      canvas.Children.Add(thumb);
      return thumb.Bl().CenterPosition;
    }*/
    /// <summary>
    /// make a line with two thumbs at the ends
    /// </summary>
    /// <param name="canvas">which canvas the line is on</param>
    /// <param name="color">the color of thumb</param>
    /// <param name="start">the start point of this line</param>
    /// <param name="end">the end point of this line</param>
    /// <returns></returns>
    /*
    public static Line MakeLine(Canvas canvas, BrushBl color, Point start, Point end) {
      var lineA = new Line() { Stroke = Brushes.Black, StrokeThickness = 1 };
      canvas.Children.Add(lineA);
      lineA.Bl().Start = MakeThumb(canvas, color, true).Init(start);
      lineA.Bl().End = MakeThumb(canvas, color, true).Init(end);
      return lineA;
    }*/

    /// <summary>
    /// a simple test to show if two lines intersect
    /// </summary>
    /// <param name="canvas"></param>
    public static void IntersectionTest(Canvas canvas) {
      Func<Brush, Point, Point, LineBl> MakeLine = (b, p0, p1) => new LineBl(canvas) {
        HasThumbs = true,
        Start = p0, End = p1,
        Stroke = { Brush = b, Thickness = 1 }
      };
      var lineA = MakeLine(Brushes.Red, new Point(10, 10), new Point(100, 100));
      var lineB = MakeLine(Brushes.Blue, new Point(10, 100), new Point(100, 10));
      var intersect = new ThumbBl(canvas) { Background = Brushes.Green};


      intersect.CenterPosition = Intersect(lineA.Start, lineA.End, lineB.Start, lineB.End);
    }

    /// <summary>
    /// a simple test to show the Circle effect of the blue point whose spring center is the green point
    /// </summary>
    /// <param name="canvas"></param>
    public static void SpringTest(CanvasBl canvas) {
      var pA = new ThumbBl(canvas) { CanDrag = true , Background = Brushes.Green, CenterPosition = { Now = new PointBl(500, 500) } };
      var pB = new ThumbBl(canvas) { CanDrag = true , Background = Brushes.Red  , CenterPosition = { Now = new PointBl(100, 100) } };
      var pC = new ThumbBl(canvas) { CanDrag = false, Background = Brushes.Blue , 
        CenterPosition = pB.CenterPosition.Spring(pA, 100) };
        
    }
    /// <summary>
    /// a simple test to show the angles (0~360) 
    /// </summary>
    /// <param name="canvas"></param>
    public static void AngleTest(CanvasBl canvas) {
      var pA = new ThumbBl(canvas) { Background = Brushes.Green, CanDrag = true, CenterPosition = { Now = new PointBl(500, 500) } };
      var pC = new ThumbBl(canvas) { Background = Brushes.Blue , CanDrag = false };
      var slider = new SliderBl(canvas) { Minimum = 0, Maximum = 360, Width = canvas.Width, RightBottom = canvas.Size };

      pC.CenterPosition = RadianAngleBl.FromDegrees(slider.Value).ToRadians().ToPoint() * 100 + pA.CenterPosition;

    }

    /// <summary>
    /// a simple test to show the triangle base(B,C) and its intersecting point with height form point A
    /// </summary>
    /// <param name="canvas"></param>
    public static void WithinTest(Canvas canvas) {
      var pA = new ThumbBl(canvas) { Background = Brushes.Green, CanDrag = true, CenterPosition = { Now = new PointBl(100, 10) } };
      var pB = new ThumbBl(canvas) { Background = Brushes.Green, CanDrag = true, CenterPosition = { Now = new PointBl(400, 100) } };
      var pC = new ThumbBl(canvas) { Background = Brushes.Green, CanDrag = true, CenterPosition = { Now = new PointBl(400, 500) } };
      var pD = new ThumbBl(canvas) { Background = Brushes.Green, CanDrag = true, CenterPosition = { Now = new PointBl(100, 300) } };

      new LineBl(canvas) { Start = pA, End = pB };
      new LineBl(canvas) { Start = pB, End = pC };
      new LineBl(canvas) { Start = pC, End = pD };
      new LineBl(canvas) { Start = pD, End = pA };

      var testPoint = new ThumbBl(canvas) { 
        Background = Brushes.Blue, CanDrag = true, CenterPosition = { Now = new PointBl(200, 200) } };


      new LabelBl(canvas) {
        LeftBottom = new PointBl(0, canvas.Bl().Height),
        Content = ObjectBl.UpCast<bool>(testPoint.CenterPosition.Within(pA, pB, pC, pD)),
      };

    }

    /// <summary>
    /// a simple test to show the triangle base(B,C) and its intersecting point with height form point A
    /// </summary>
    /// <param name="canvas"></param>
    public static void TriangleBaseTest(Canvas canvas) {
      var pA = new ThumbBl(canvas) { Background = Brushes.Green, CanDrag = true, CenterPosition = { Now = new PointBl(10, 10) } };
      var pB = new ThumbBl(canvas) { Background = Brushes.Red  , CanDrag = true, CenterPosition = { Now = new PointBl(40, 100) } };
      var pC = new ThumbBl(canvas) { Background = Brushes.Blue, CanDrag = true, CenterPosition = { Now = new PointBl(200, 300) } };

      var pD = new ThumbBl(canvas) {
        Background = Brushes.Purple, CanDrag = false,
        CenterPosition = pA.CenterPosition.TriangleBase(pB, pC),
      };

    }

    /// <summary>
    /// a simple test to show the distance of point A to the line denoted by (pStart, pEnd)
    /// </summary>
    /// <param name="canvas"></param>    
    public static void DistanceTest(CanvasBl canvas) {
      var pA = new ThumbBl(canvas) { Background = Brushes.Green, CanDrag = true, CenterPosition = { Now = new PointBl(10, 10) } };
      var pB = new ThumbBl(canvas) { Background = Brushes.Red, CanDrag = true, CenterPosition = { Now = new PointBl(400, 100) } };
      var pC = new ThumbBl(canvas) { Background = Brushes.Blue, CanDrag = true, CenterPosition = { Now = new PointBl(200, 300) } };


      new LineBl(canvas) { Start = pA, End = pB };
      new LineBl(canvas) { Start = pA, End = pC };
      new LineBl(canvas) { Start = pB, End = pC };

      new LineBl(canvas) {
        Start = pA, End = new ThumbBl(canvas) {
          Background = Brushes.Purple,
          CenterPosition = pA.CenterPosition.TriangleBase(pB, pC)
        }
      };

      new LabelBl(canvas) {
        LeftBottom = new PointBl(0, canvas.Height),
        Content = ObjectBl.UpCast<double>(pA.CenterPosition.Distance(pB, pC)),
      };
      new LabelBl(canvas) {
        RightBottom = canvas.Size,
        Content = ObjectBl.UpCast<double>((pA - pA.CenterPosition.TriangleBase(pB, pC)).Length),
      };
      //Console.WriteLine("distance=" + dist);
    }


    /// <summary>
    /// a simple test to show the triangle base(B,C) and its intersecting point with height form point A
    /// </summary>
    /// <param name="canvas"></param>
    public static void MirrorPointTest(Canvas canvas) {
      var pA = new ThumbBl(canvas) { Background = Brushes.Green, CanDrag = true, CenterPosition = { Now = new PointBl(10, 10) } };
      var pB = new ThumbBl(canvas) { Background = Brushes.Red  , CanDrag = true, CenterPosition = { Now = new PointBl(400, 100) } };
      var pC = new ThumbBl(canvas) { Background = Brushes.Blue, CanDrag = true, CenterPosition = { Now = new PointBl(200, 300) } };

      var pD = new ThumbBl(canvas) { Background = Brushes.Purple, CanDrag = false, 
        CenterPosition = pA.CenterPosition.MirrorPoint(pB,pC),
      };
    }

    /// <summary>
    /// a simple test to show the bezier curve and its color also changes with bezier fuction
    /// </summary>
    /// <param name="canvas"></param>
    public static void BezierTest(Canvas canvas) {
      var rect = new Rectangle() { Width = 100, Height = 100 };
      var slider = new Slider() { Maximum = 1, Minimum = 0 };

      var t = slider.Bl().Value.Bezier(0d, 0.1, .3, .5, .7, 1.0d);

      //Console.WriteLine("BEZIER: " + t);

      var color = t.Bezier(Colors.Red, Colors.Blue, Colors.Orange);

      rect.Bl().Fill = color;

      var p = t.Bezier(
                                new PointBl(0, canvas.Bl().Height / 2),
                                new PointBl(canvas.Bl().Width*.25, 100),
                                new PointBl(canvas.Bl().Width*.75, 600),
                                new PointBl(canvas.Bl().Width, canvas.Bl().Height / 2));

      rect.Bl().CenterPosition = p;

      canvas.Children.Add(rect);
      slider.Bl().Width = canvas.Bl().Width;
      slider.Bl().LeftBottom = new PointBl(0, canvas.Bl().Height);
      canvas.Children.Add(slider);

    }


  }
}
namespace Bling.Geom {
  public static class GeometryExtensions {
    // XXX: probably deprecated.
    public static PointBl MidPoint(this QuadraticBezierSegment source, PointBl startPoint, DoubleBl t) {
      return MidPoint(startPoint, source.Bl().Point1, source.Bl().Point2, t);
    }
    public static PointBl MidPoint(PointBl startPoint, PointBl controlPoint, PointBl endPoint, DoubleBl t) {
      var tA = startPoint * (1d - t).Pow(2);
      var tX = 2d * t * (1d - t);
      var tB = controlPoint * tX;
      var tC = endPoint * t.Pow(2);
      var ret = (tB + (tA + (tC)));
      return ret;
    }
    /// <summary>
    /// Create a bezier polygon segment with four points starting at the second point but curved for the first, and ending at the third point but curved for the fourth. 
    /// </summary>
    /// <param name="smoothValue">Sharpness of the polygon edge (0.8 is a good default)</param>
    /// <param name="Points">Four points that define the polygon segment.</param>
    /// <returns>A bezier segment that is curved through points 2 and 3 with tangents according to points 1 and 4.</returns>
    public static BezierSegmentBl PolygonSegment(DoubleBl smoothValue, params PointBl[] Points) {
      (Points.Length == 4).Assert();
      return PolygonSegment(Points[0], Points[1], Points[2], Points[3], smoothValue);
    }

    /// <summary>
    /// Create a bezier polygon segment with four points starting at the second point but curved for the first, and ending at the third point but curved for the fourth. 
    /// </summary>
    /// <param name="Points">Four points that define the polygon segment.</param>
    /// <returns>A bezier segment that is curved through points 2 and 3 with tangents according to points 1 and 4.</returns>
    //public static BezierSegmentBl PolygonSegment(params PointBl[] Points) {
    //  return PolygonSegment(0.8, Points);
    //}
    private static BezierSegmentBl PolygonSegment(PointBl point0, PointBl point1, PointBl point2, PointBl point3, DoubleBl smooth) {
      var c1 = (point0 + point1) / 2d;
      var c2 = (point1 + point2) / 2d;
      var c3 = (point2 + point3) / 2d;

      var len1 = (point1 - point0).Length;
      var len2 = (point2 - point1).Length;
      var len3 = (point3 - point2).Length;

      var k1 = len1 / (len1 + len2);
      var k2 = len2 / (len2 + len3);

      var m1 = c1 + (c2 - c1) * k1;
      var m2 = c2 + (c3 - c2) * k2;

      var ctrl1 = m1 + (c2 - m1) * smooth + point1 - m1;
      var ctrl2 = m2 + (c2 - m2) * smooth + point2 - m2;
      var curve = new BezierSegmentBl();
      curve.Point1 = ctrl1;
      curve.Point2 = ctrl2;
      curve.Point3 = point2;
      return curve;
    }
    private static QuadraticBezierSegmentBl PolygonSegment2(PointBl point0, PointBl point1, PointBl point2, PointBl point3, DoubleBl smooth) {
      var c1 = (point0 + point1) / 2d;
      var c2 = (point1 + point2) / 2d;
      var c3 = (point2 + point3) / 2d;

      var len1 = (point1 - point0).Length;
      var len2 = (point2 - point1).Length;
      var len3 = (point3 - point2).Length;

      var k1 = len1 / (len1 + len2);
      var k2 = len2 / (len2 + len3);

      var m1 = c1 + (c2 - c1) * k1;
      var m2 = c2 + (c3 - c2) * k2;

      var ctrl1 = m1 + (c2 - m1) * smooth + point1 - m1;
      var ctrl2 = m2 + (c2 - m2) * smooth + point2 - m2;
      var curve = new QuadraticBezierSegmentBl();
      curve.Point1 = (ctrl1 + ctrl2) / 2d;
      //curve.Point2 = ctrl2;
      curve.Point2 = point2;
      return curve;
    }
  }


}
